﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. 
// See License.txt in the project root for license information.
using System;
using System.IO;
using System.Net;
using System.Threading.Actors.Utilities;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Networking;
using Windows.Networking.Sockets;

namespace System.Threading.Actors.WinRTSupport
{
    // This is the Phone version of WinRTSocketProvider.  We have a separate implementation for Windows Store apps.
    // This is the same source file, but we need to compile it differently for each platform.
    // They should be ideally exactly the same.  However, .NET's Portable Library plumbing doesn't yet
    // support WinRT API's.  Hopefully that will be fixed in an update (Q1 2014?), and we can re-merge our 
    // WinRT support libraries into one.
    public class WinRTSocketProvider : ITcpClient
    {
        private StreamSocket _socket;
        private Stream _inputStream;
        private Stream _outputStream;
        private int _port;

        public WinRTSocketProvider(string remoteHost, int port)
        {
            if (_socket != null)  // Assume the connection is bad?
                _socket.Dispose();
            _port = port;
            _socket = new StreamSocket();
            // Note the CLR's WinRT extension methods will return a BufferedStream around the WinRT 
            // IOutputStream or IInputStream, depending on whether the buffer size is > 0.  
            // BufferedStream will require us to call Flush in a few places, and may break
            // the semantics of this NoDelay setting.  Skipping that BufferedStream is probably a good idea.
            _socket.Control.NoDelay = true;
            HostName hostName = new HostName(remoteHost);
            // I tried setting SocketProtectionLevel.SslAllowNullEncryption, but that caused the connection to hang.  Perhaps
            // because the other remote endpoint was a normal .NET Socket and not a WinRT StreamSocket?
            _socket.ConnectAsync(hostName, port.ToString()).AsTask().Wait();

            _inputStream = _socket.InputStream.AsStreamForRead(0);
            _outputStream = _socket.OutputStream.AsStreamForWrite(0);
        }

        public Task ConnectAsync(String remoteHost, int port)
        {
            if (_socket != null)  // Assume the connection is bad?
                _socket.Dispose();

            _socket = new StreamSocket();
            // Note the CLR's WinRT extension methods will return a BufferedStream around the WinRT 
            // IOutputStream or IInputStream, depending on whether the buffer size is > 0.  
            // BufferedStream will require us to call Flush in a few places, and may break
            // the semantics of this NoDelay setting.  Skipping that BufferedStream is probably a good idea.
            _socket.Control.NoDelay = true;
            HostName hostName = new HostName(remoteHost);
            // I tried setting SocketProtectionLevel.SslAllowNullEncryption, but that caused the connection to hang.  Perhaps
            // because the other remote endpoint was a normal .NET Socket and not a WinRT StreamSocket?
            return _socket.ConnectAsync(hostName, port.ToString()).AsTask();
        }

        public Stream InputStream {
            get { return _inputStream; }
        }

        public Stream OutputStream {
            get { return _outputStream; }
        }

        public int Port {
            get { return _port; }
        }

        public void Dispose()
        {
            if (_socket != null)
            {
                if (_inputStream != null)
                {
                    _inputStream.Dispose();
                    _inputStream = null;
                }

                if (_outputStream != null)
                {
                    _outputStream.Dispose();
                    _outputStream = null;
                }

                _socket.Dispose();
                _socket = null;
            }
        }

        public void Close()
        {
            Dispose();
        }

        //
        // Some magic to insert WinRTSocketProvider as the TCP provider of choice in the portable library
        //

        private static ITcpClient CreateTcpClient(string hostname, int port)
        {
            return new WinRTSocketProvider(hostname, port);
        }

        static WinRTSocketProvider()
        {
            LightupITcpClientFactory.CreateTcpClientFunc = CreateTcpClient;
        }
    }
}
